<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>31.2. Zend_OpenId_Consumer Basics</title>
<link rel="stylesheet" href="dbstyle.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.72.0">
<link rel="start" href="index.html" title="Programmer's Reference Guide">
<link rel="up" href="zend.openid.html" title="Chapter 31. Zend_OpenId">
<link rel="prev" href="zend.openid.html" title="Chapter 31. Zend_OpenId">
<link rel="next" href="zend.openid.provider.html" title="31.3. Zend_OpenId_Provider">
<link rel="chapter" href="introduction.html" title="Chapter 1. Introduction to Zend Framework">
<link rel="chapter" href="zend.acl.html" title="Chapter 2. Zend_Acl">
<link rel="chapter" href="zend.auth.html" title="Chapter 3. Zend_Auth">
<link rel="chapter" href="zend.cache.html" title="Chapter 4. Zend_Cache">
<link rel="chapter" href="zend.config.html" title="Chapter 5. Zend_Config">
<link rel="chapter" href="zend.console.getopt.html" title="Chapter 6. Zend_Console_Getopt">
<link rel="chapter" href="zend.controller.html" title="Chapter 7. Zend_Controller">
<link rel="chapter" href="zend.currency.html" title="Chapter 8. Zend_Currency">
<link rel="chapter" href="zend.date.html" title="Chapter 9. Zend_Date">
<link rel="chapter" href="zend.db.html" title="Chapter 10. Zend_Db">
<link rel="chapter" href="zend.debug.html" title="Chapter 11. Zend_Debug">
<link rel="chapter" href="zend.dojo.html" title="Chapter 12. Zend_Dojo">
<link rel="chapter" href="zend.dom.html" title="Chapter 13. Zend_Dom">
<link rel="chapter" href="zend.exception.html" title="Chapter 14. Zend_Exception">
<link rel="chapter" href="zend.feed.html" title="Chapter 15. Zend_Feed">
<link rel="chapter" href="zend.filter.html" title="Chapter 16. Zend_Filter">
<link rel="chapter" href="zend.form.html" title="Chapter 17. Zend_Form">
<link rel="chapter" href="zend.gdata.html" title="Chapter 18. Zend_Gdata">
<link rel="chapter" href="zend.http.html" title="Chapter 19. Zend_Http">
<link rel="chapter" href="zend.infocard.html" title="Chapter 20. Zend_InfoCard">
<link rel="chapter" href="zend.json.html" title="Chapter 21. Zend_Json">
<link rel="chapter" href="zend.layout.html" title="Chapter 22. Zend_Layout">
<link rel="chapter" href="zend.ldap.html" title="Chapter 23. Zend_Ldap">
<link rel="chapter" href="zend.loader.html" title="Chapter 24. Zend_Loader">
<link rel="chapter" href="zend.locale.html" title="Chapter 25. Zend_Locale">
<link rel="chapter" href="zend.log.html" title="Chapter 26. Zend_Log">
<link rel="chapter" href="zend.mail.html" title="Chapter 27. Zend_Mail">
<link rel="chapter" href="zend.measure.html" title="Chapter 28. Zend_Measure">
<link rel="chapter" href="zend.memory.html" title="Chapter 29. Zend_Memory">
<link rel="chapter" href="zend.mime.html" title="Chapter 30. Zend_Mime">
<link rel="chapter" href="zend.openid.html" title="Chapter 31. Zend_OpenId">
<link rel="chapter" href="zend.paginator.html" title="Chapter 32. Zend_Paginator">
<link rel="chapter" href="zend.pdf.html" title="Chapter 33. Zend_Pdf">
<link rel="chapter" href="zend.registry.html" title="Chapter 34. Zend_Registry">
<link rel="chapter" href="zend.rest.html" title="Chapter 35. Zend_Rest">
<link rel="chapter" href="zend.search.lucene.html" title="Chapter 36. Zend_Search_Lucene">
<link rel="chapter" href="zend.server.html" title="Chapter 37. Zend_Server">
<link rel="chapter" href="zend.service.html" title="Chapter 38. Zend_Service">
<link rel="chapter" href="zend.session.html" title="Chapter 39. Zend_Session">
<link rel="chapter" href="zend.soap.html" title="Chapter 40. Zend_Soap">
<link rel="chapter" href="zend.test.html" title="Chapter 41. Zend_Test">
<link rel="chapter" href="zend.text.html" title="Chapter 42. Zend_Text">
<link rel="chapter" href="zend.timesync.html" title="Chapter 43. Zend_TimeSync">
<link rel="chapter" href="zend.translate.html" title="Chapter 44. Zend_Translate">
<link rel="chapter" href="zend.uri.html" title="Chapter 45. Zend_Uri">
<link rel="chapter" href="zend.validate.html" title="Chapter 46. Zend_Validate">
<link rel="chapter" href="zend.version.html" title="Chapter 47. Zend_Version">
<link rel="chapter" href="zend.view.html" title="Chapter 48. Zend_View">
<link rel="chapter" href="zend.xmlrpc.html" title="Chapter 49. Zend_XmlRpc">
<link rel="appendix" href="requirements.html" title="Appendix A. Zend Framework Requirements">
<link rel="appendix" href="coding-standard.html" title="Appendix B. Zend Framework Coding Standard for PHP">
<link rel="appendix" href="copyrights.html" title="Appendix C. Copyright Information">
<link rel="index" href="the.index.html" title="Index">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.authentication" title="31.2.1. OpenID Authentication">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.combine" title="31.2.2. Combine all Steps in One Page">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.realm" title="31.2.3. Realm">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.check" title="31.2.4. Immediate Check">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.storage" title="31.2.5. Zend_OpenId_Consumer_Storage">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.sreg" title="31.2.6. Simple Registration Extension">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.auth" title="31.2.7. Integration with Zend_Auth">
<link rel="subsection" href="zend.openid.consumer.html#zend.openid.consumer.mvc" title="31.2.8. Integration with Zend_Controller">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader"><table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">31.2. Zend_OpenId_Consumer Basics</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="zend.openid.html">Prev</a> </td>
<th width="60%" align="center">Chapter 31. Zend_OpenId</th>
<td width="20%" align="right"> <a accesskey="n" href="zend.openid.provider.html">Next</a>
</td>
</tr>
</table></div>
<div class="sect1" lang="en">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="zend.openid.consumer"></a>31.2. Zend_OpenId_Consumer Basics</h2></div></div></div>
<p>
        <code class="code">Zend_OpenId_Consumer</code> is used to implement the OpenID
        authentication schema on web sites.
    </p>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.authentication"></a>31.2.1. OpenID Authentication</h3></div></div></div>
<p>
            From a site developers point of view, the OpenID authentication
            process consists of three steps:
        </p>
<div class="orderedlist"><ol type="1">
<li><p>
                    Show OpenID authentication form.
                </p></li>
<li><p>
                    Accept OpenID identity and pass it to the OpenID provider.
                </p></li>
<li><p>
                    Verify response from the OpenID provider.
                </p></li>
</ol></div>
<p>
            In actual fact the OpenID authentication protocol performs more
            steps, but most of them are encapsulated inside the
            <code class="code">Zend_OpenId_Consumer</code>, and they are transparent to the
            developer.
        </p>
<p>
            The OpenID authentication process is initiated by the end-user by
            filling in their identification into the appropriate form and submiting
            it. The following example shows a simple form that accepts an OpenID
            identifier. Note that the example shows only a login.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-1"></a><p class="title"><b>Example 31.1. The Simple OpenID Login form</b></p>
<div class="example-contents"><pre class="programlisting">&lt;html&gt;&lt;body&gt;
&lt;form method="post" action="example-1_2.php"&gt;&lt;fieldset&gt;
&lt;legend&gt;OpenID Login&lt;/legend&gt;
&lt;input type="text" name="openid_identifier"&gt;
&lt;input type="submit" name="openid_action" value="login"&gt;
&lt;/fieldset&gt;&lt;/form&gt;&lt;/body&gt;&lt;/html&gt;
            </pre></div>
</div>
<br class="example-break"><p>
            On submit this form passes the OpenID identity to the following PHP
            script that performs a second step of authentication. The only thing the
            PHP script needs to do in this step is call the
            <code class="code">Zend_OpenId_Consumer::login()</code> method. The first argument of this
            method is an accepted OpenID identity and the second is a URL of a script
            that handles the third and last step of authentication.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-1_2"></a><p class="title"><b>Example 31.2. The Authentication Request Handler</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";

$consumer = new Zend_OpenId_Consumer();
if (!$consumer-&gt;login($_POST['openid_identifier'], 'example-1_3.php')) {
    die("OpenID login failed.");
}
            </pre></div>
</div>
<br class="example-break"><p>
            The <code class="code">Zend_OpenId_Consumer::login()</code> performs discovery on
            a given identifier and on success, finds out the address of the identity
            provider and its local identifier. Then, it creates an association to the
            given provider so that both the site and provider know the same secret
            that is used to sign the following messages. Then it passes an
            authentication request to the provider. Note this request redirects the
            end-user's web browser to an OpenID server site, where users are able to
            continue the authentication process.
        </p>
<p>
            An OpenID Server usually asks users for; their password (if they
            weren't previously logged-in), if the user trusts this site and what
            information may be returned to the site. These interactions are not
            visible to the OpenID-enabled site so there is no what for it to get the
            user's password or other information that was not opened.
        </p>
<p>
            On success, <code class="code">Zend_OpenId_Consumer::login()</code> never
            returns, because it performs an HTTP redirection, however in case of error
            it may return false. Errors may occure due to an invalid identity, dead
            provider, communication error, etc
        </p>
<p>
            The third step of authentication is initiated by a response from the
            OpenID provider, after it has already authenticated the user's password.
            This response is passed indirectly, as an HTTP redirection of the
            end-user's web browser. And the only thing that site must do is to check
            if this response is valid.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-1_3"></a><p class="title"><b>Example 31.3. The Authentication Response Verifier</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";

$consumer = new Zend_OpenId_Consumer();
if ($consumer-&gt;verify($_GET, $id)) {
    echo "VALID " . htmlspecialchars($id);
} else {
    echo "INVALID " . htmlspecialchars($id);
}
            </pre></div>
</div>
<br class="example-break"><p>
            This check is performed using the <code class="code">Zend_OpenId_Consumer::verify</code>
            method, that takes the whole array of
            the HTTP request's arguments and checks if this response is properly
            signed by an appropriate OpenID provider. It also may assign
            the claimed OpenID identity that was entered by end-user in the
            first step into the second (optional) argument.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.combine"></a>31.2.2. Combine all Steps in One Page</h3></div></div></div>
<p>
            The following example combines all three steps together. It doesn't
            provide any additional functionality. The only advantage is that now
            developers don't need to specify any URL's of scripts that handle the next
            step. By default, all steps use the same URL. However, the script now
            includes a dispatch code that calls appropriate code for each step of
            authentication.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-2"></a><p class="title"><b>Example 31.4. The Complete OpenID Login Script</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";

$status = "";
if (isset($_POST['openid_action']) &amp;&amp;
    $_POST['openid_action'] == "login" &amp;&amp;
    !empty($_POST['openid_identifier'])) {

    $consumer = new Zend_OpenId_Consumer();
    if (!$consumer-&gt;login($_POST['openid_identifier'])) {
        $status = "OpenID login failed.&lt;br&gt;";
    }
} else if (isset($_GET['openid_mode'])) {
    if ($_GET['openid_mode'] == "id_res") {
        $consumer = new Zend_OpenId_Consumer();
        if ($consumer-&gt;verify($_GET, $id)) {
            $status = "VALID " . htmlspecialchars($id);
        } else {
            $status = "INVALID " . htmlspecialchars($id);
        }
    } else if ($_GET['openid_mode'] == "cancel") {
        $status = "CANCELED";
    }
}
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;?php echo "$status&lt;br&gt;";?&gt;
&lt;form method="post"&gt;&lt;fieldset&gt;
&lt;legend&gt;OpenID Login&lt;/legend&gt;
&lt;input type="text" name="openid_identifier" value=""&gt;
&lt;input type="submit" name="openid_action" value="login"&gt;
&lt;/fieldset&gt;&lt;/form&gt;&lt;/body&gt;&lt;/html&gt;
            </pre></div>
</div>
<br class="example-break"><p>
            In addition, this code differenciates between canceled and wrong
            authentication responses. The provider retuns a canceled responce in cases
            when an identity provider doesn't know the supplied identity or the user
            is not logged-in or they don't trust the site. A wrong response assumes
            that the responce is wrong or incorrectly signed.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.realm"></a>31.2.3. Realm</h3></div></div></div>
<p>
            When an OpenID-enabled site passes authentication requests to a
            provider, it identifies itself with a realm URL. This URL may be
            considered as a root of a trusted site. If the user trusts the URL they
            will also trust to matched and subsequent URLs.
        </p>
<p>
            By default, the realm URL is automatically set to the URL of the
            directory where the login script is. This decision is useful for most, but
            not all cases. Sometimes a whole site and not directory is used, or even a
            combination of several servers from one domain.
        </p>
<p>
            To implement this ability, developers may pass the realm value as a
            third argument to the <code class="code">Zend_OpenId_Consumer::login</code> method. In
            the following example the single interaction asks for trusted access to
            all php.net sites.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-3_2"></a><p class="title"><b>Example 31.5. Authentication Request for Specified Realm</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";

$consumer = new Zend_OpenId_Consumer();
if (!$consumer-&gt;login($_POST['openid_identifier'], 'example-3_3.php', 'http://*.php.net/')) {
    die("OpenID login failed.");
}
            </pre></div>
</div>
<br class="example-break"><p>
            The example below only implements the second step of authentication,
            the first and third steps are the same as in the first example.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.check"></a>31.2.4. Immediate Check</h3></div></div></div>
<p>
            In some situations it is necissary to see if a user is already
            logged-in into a trusted OpenID server without any interaction with the
            user. The <code class="code">Zend_OpenId_Consumer::check</code> method does precisely
            that. It is executed with exactly the same arguments as
            <code class="code">Zend_OpenId_Consumer::login</code> but it doesn't show the user any
            OpenID server pages. Therefore from the users point of view it is
            transparent and it seems as if they never left the site. The third step
            succeedes if user is already logged-in and trusted to the site otherwise
            it will fail.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-4"></a><p class="title"><b>Example 31.6. Immediate Check without Interaction</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";

$consumer = new Zend_OpenId_Consumer();
if (!$consumer-&gt;check($_POST['openid_identifier'], 'example-4_3.php')) {
    die("OpenID login failed.");
}
            </pre></div>
</div>
<br class="example-break"><p>
            The example below only implements the second step of authentication,
            first and third steps are the same as in the first example.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.storage"></a>31.2.5. Zend_OpenId_Consumer_Storage</h3></div></div></div>
<p>
            There are three steps to the OpenID authentication procedure, each
            step is performed by a separate HTTP request. To store information between
            requests <code class="code">Zend_OpenId_Consumer</code> uses internal storage.
        </p>
<p>
            Developers may not care about this storage because by default
            <code class="code">Zend_OpenId_Consumer</code> uses file-based storage under /tmp
            similar to PHP sessions. However, this storage may be not suitable in all
            cases. Some may want to store information in a database while others may
            need to use common storage suitable for big web-farms. Fortunately,
            developers may easily replace the default storage with their own. The only
            thing to implement is it's own storage class as a child of
            the <code class="code">Zend_OpenId_Consumer_Storage</code> method and pass it as a first
            argument to the <code class="code">Zend_OpenId_Consumer</code> constructor.
        </p>
<p>
            The following example demonstrates a simple storage that uses
            <code class="code">Zend_Db</code> as the backend containing three groups of functions.
            the first is for working with associations, the second is to cache
            discovery information and the third is to check responce uniqueness. The
            class is implemented in such a way that it can be easily used with
            existing or new databases. If necessary, it will create database tables if
            they don't exist.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-5"></a><p class="title"><b>Example 31.7. Databse Storage</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
class DbStorage extends Zend_OpenId_Consumer_Storage
{
    private $_db;
    private $_association_table;
    private $_discovery_table;
    private $_nonce_table;

    public function __construct($db,
                                $association_table = "association",
                                $discovery_table = "discovery",
                                $nonce_table = "nonce")
    {
        $this-&gt;_db = $db;
        $this-&gt;_association_table = $association_table;
        $this-&gt;_discovery_table = $discovery_table;
        $this-&gt;_nonce_table = $nonce_table;
        $tables = $this-&gt;_db-&gt;listTables();
        if (!in_array($association_table, $tables)) {
            $this-&gt;_db-&gt;getConnection()-&gt;exec(
                "create table $association_table (" .
                " url     varchar(256) not null primary key," .
                " handle  varchar(256) not null," .
                " macFunc char(16) not null," .
                " secret  varchar(256) not null," .
                " expires timestamp" .
                ")");
        }
        if (!in_array($discovery_table, $tables)) {
            $this-&gt;_db-&gt;getConnection()-&gt;exec(
                "create table $discovery_table (" .
                " id      varchar(256) not null primary key," .
                " realId  varchar(256) not null," .
                " server  varchar(256) not null," .
                " version float," .
                " expires timestamp" .
                ")");
        }
        if (!in_array($nonce_table, $tables)) {
            $this-&gt;_db-&gt;getConnection()-&gt;exec(
                "create table $nonce_table (" .
                " nonce   varchar(256) not null primary key," .
                " created timestamp default current_timestamp" .
                ")");
        }
    }

    public function addAssociation($url, $handle, $macFunc, $secret, $expires)
    {
        $table = $this-&gt;_association_table;
        $secret = base64_encode($secret);
        $this-&gt;_db-&gt;query("insert into $table (url, handle, macFunc, secret, expires) " .
                          "values ('$url', '$handle', '$macFunc', '$secret', $expires)");
        return true;
    }

    public function getAssociation($url, &amp;$handle, &amp;$macFunc, &amp;$secret, &amp;$expires)
    {
        $table = $this-&gt;_association_table;
        $this-&gt;_db-&gt;query("delete from $table where expires &lt; " . time());
        $res = $this-&gt;_db-&gt;fetchRow("select handle, macFunc, secret, expires from $table where url = '$url'");
        if (is_array($res)) {
            $handle  = $res['handle'];
            $macFunc = $res['macFunc'];
            $secret  = base64_decode($res['secret']);
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function getAssociationByHandle($handle, &amp;$url, &amp;$macFunc, &amp;$secret, &amp;$expires)
    {
        $table = $this-&gt;_association_table;
        $this-&gt;_db-&gt;query("delete from $table where expires &lt; " . time());
        $res = $this-&gt;_db-&gt;fetchRow("select url, macFunc, secret, expires from $table where handle = '$handle'");
        if (is_array($res)) {
            $url     = $res['url'];
            $macFunc = $res['macFunc'];
            $secret  = base64_decode($res['secret']);
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function delAssociation($url)
    {
        $table = $this-&gt;_association_table;
        $this-&gt;_db-&gt;query("delete from $table where url = '$url'");
        return true;
    }

    public function addDiscoveryInfo($id, $realId, $server, $version, $expires)
    {
        $table = $this-&gt;_discovery_table;
        $this-&gt;_db-&gt;query("insert into $table (id, realId, server, version, expires) " .
                          "values ('$id', '$realId', '$server', $version, $expires)");
        return true;
    }

    public function getDiscoveryInfo($id, &amp;$realId, &amp;$server, &amp;$version, &amp;$expires)
    {
        $table = $this-&gt;_discovery_table;
        $this-&gt;_db-&gt;query("delete from $table where expires &lt; " . time());
        $res = $this-&gt;_db-&gt;fetchRow("select realId, server, version, expires from $table where id = '$id'");
        if (is_array($res)) {
            $realId  = $res['realId'];
            $server  = $res['server'];
            $version = $res['version'];
            $expires = $res['expires'];
            return true;
        }
        return false;
    }

    public function delDiscoveryInfo($id)
    {
        $table = $this-&gt;_discovery_table;
        $this-&gt;_db-&gt;query("delete from $table where id = '$id'");
        return true;
    }

    public function isUniqueNonce($nonce)
    {
        $table = $this-&gt;_nonce_table;
        try {
            $ret = $this-&gt;_db-&gt;query("insert into $table (nonce) values ('$nonce')");
        } catch (Zend_Db_Statement_Exception $e) {
            return false;
        }
        return true;
    }

    public function purgeNonces($date=null)
    {
    }
}

$db = Zend_Db::factory('Pdo_Sqlite',
    array('dbname'=&gt;'/tmp/openid_consumer.db'));
$storage = new DbStorage($db);
$consumer = new Zend_OpenId_Consumer($storage);
            </pre></div>
</div>
<br class="example-break"><p>
            The example doesn't include OpenID authentication code itself, but
            it is based on the same logic as in the previous or following
            examples.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.sreg"></a>31.2.6. Simple Registration Extension</h3></div></div></div>
<p>
            In addition to authentication, the OpenID can be used for
            light-weight profile exchange. This feature is not covered by OpenID
            authentication specification but by the OpenID Simple Registration
            Extension protocol. This protocol allows OpenID-enabled sites to ask for
            information about an end-user from OpenID providers. Such information may
            include:
        </p>
<div class="itemizedlist"><ul type="disc">
<li><p>
                    <span class="emphasis"><em>nickname</em></span>
                    - any UTF-8 string that the end user wants to use as a nickname.
                </p></li>
<li><p>
                    <span class="emphasis"><em>email</em></span>
                    - the email address of the end user as specified in section 3.4.1
                    of RFC2822.
                </p></li>
<li><p>
                    <span class="emphasis"><em>fullname</em></span>
                    - a UTF-8 string representation of the end user's full name.
                </p></li>
<li><p>
                    <span class="emphasis"><em>dob</em></span>
                    - the end user's date of birth as YYYY-MM-DD. Any values whose
                    representation uses fewer than the specified number of digits
                    should be zero-padded. The length of this value must always be
                    10. If the end user does not want to reveal any particular
                    component of this value, it must be set to zero. For instance,
                    if a end user wants to specify that his date of birth is in 1980,
                    but not the month or day, the value returned shall be "1980-00-00".
                </p></li>
<li><p>
                    <span class="emphasis"><em>gender</em></span>
                    - the end user's gender, "M" for male, "F" for female.
                </p></li>
<li><p>
                    <span class="emphasis"><em>postcode</em></span>
                    - UTF-8 string that should conform to the end user's country's
                    postal system.
                </p></li>
<li><p>
                    <span class="emphasis"><em>country</em></span>
                    - the End User's country of residence as specified by ISO3166.
                </p></li>
<li><p>
                    <span class="emphasis"><em>language</em></span>
                    - end User's preferred language as specified by ISO639.
                </p></li>
<li><p>
                    <span class="emphasis"><em>timezone</em></span>
                    - ASCII string from TimeZone database. For example,
                    "Europe/Paris" or "America/Los_Angeles".
                </p></li>
</ul></div>
<p>
            An OpenID-enabled web site may ask for any combination of these
            fields. It may also strictly require some information and allow end-users
            to provide or hide other information. The following example creates an
            object of the <code class="code">Zend_OpenId_Extension_Sreg</code> class that requires
            a <span class="emphasis"><em>nickname</em></span> and optionally ask for
            <span class="emphasis"><em>email</em></span> and <span class="emphasis"><em>fullname</em></span>.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-6_2"></a><p class="title"><b>Example 31.8. Sending Requests with a Simple Registration Extension</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";
require_once "Zend/OpenId/Extension/Sreg.php";

$sreg = new Zend_OpenId_Extension_Sreg(array(
    'nickname'=&gt;true,
    'email'=&gt;false,
    'fullname'=&gt;false), null, 1.1);
$consumer = new Zend_OpenId_Consumer();
if (!$consumer-&gt;login($_POST['openid_identifier'], 'example-6_3.php', null, $sreg)) {
    die("OpenID login failed.");
}
            </pre></div>
</div>
<br class="example-break"><p>
            As you can see the <code class="code">Zend_OpenId_Extension_Sreg</code>
            constructor accepts an array of asked fields. This array has the names of
            fields as indexes and requirements flag as values.
            <span class="emphasis"><em>true</em></span> means the field is required and
            <span class="emphasis"><em>false</em></span> means the field is optional. The
            <code class="code">Zend_OpenId_Consumer::login</code> accepts extensions or list of
            extensions as a fourth argument.
        </p>
<p>
            On the third step of authentication, the
            <code class="code">Zend_OpenId_Extension_Sreg</code> object should be passed to
            <code class="code">Zend_OpenId_Consumer::verify</code>. Then on successful authentication
            <code class="code">Zend_OpenId_Extension_Sreg::getProperties</code> will return an
            associative array of requested fields.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-6_3"></a><p class="title"><b>Example 31.9. Verifying Responses with a Simple Registration Extension</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";
require_once "Zend/OpenId/Extension/Sreg.php";

$sreg = new Zend_OpenId_Extension_Sreg(array(
    'nickname'=&gt;true,
    'email'=&gt;false,
    'fullname'=&gt;false), null, 1.1);
$consumer = new Zend_OpenId_Consumer();
if ($consumer-&gt;verify($_GET, $id, $sreg)) {
    echo "VALID " . htmlspecialchars($id) ."&lt;br&gt;\n";
    $data = $sreg-&gt;getProperties();
    if (isset($data['nickname'])) {
        echo "nickname: " . htmlspecialchars($data['nickname']) . "&lt;br&gt;\n";
    }
    if (isset($data['email'])) {
        echo "email: " . htmlspecialchars($data['email']) . "&lt;br&gt;\n";
    }
    if (isset($data['fullname'])) {
        echo "fullname: " . htmlspecialchars($data['fullname']) . "&lt;br&gt;\n";
    }
} else {
    echo "INVALID " . htmlspecialchars($id);
}
            </pre></div>
</div>
<br class="example-break"><p>
            If <code class="code">Zend_OpenId_Extension_Sreg</code> was created without any
            arguments, the user code should check for the existence of the required
            data itself. However, if the object is created with the same list of
            required fields as on the second step, it will automatically check for the
            existence of required data. In this case, <code class="code">Zend_OpenId_Consumer::verify</code>
             will return <span class="emphasis"><em>false</em></span> if any of the required fields are
            missing.
        </p>
<p>
            By default, <code class="code">Zend_OpenId_Extension_Sreg</code> uses version
            1.0, because the specification for version 1.1 is not yet finalized.
            However, some libraries don't fully support version 1.0. For example,
            www.myopenid.com requires an SREG namespace in requests which is only
            available in 1.1. To work with this server, explicitly set the version to
            1.1 in the <code class="code">Zend_OpenId_Extension_Sreg</code> constructor.
        </p>
<p>
            The second argument of the <code class="code">Zend_OpenId_Extension_Sreg</code>
            constructor is a policy URL, that should be provided to the end-user by
            the identity provider.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.auth"></a>31.2.7. Integration with Zend_Auth</h3></div></div></div>
<p>
            Zend Framework provides a special class to support user
            authentication - <code class="code">Zend_Auth</code>. This class can be used together
            with <code class="code">Zend_OpenId_Consumer</code>. The following example shows how
            <code class="code">OpenIdAdapter</code> implements
            the <code class="code">Zend_Auth_Adapter_Interface</code> with the
            <code class="code">authenticate</code> method.This performs an authentication query and
            verification.
        </p>
<p>
            The big difference between this adapter and existing ones, is that
            it works on two HTTP requests and includes a dispatch code to perform the
            second or third step of OpenID authentication.
        </p>
<div class="example">
<a name="zend.openid.consumer.example-7"></a><p class="title"><b>Example 31.10. Zend_Auth Adapter for OpenID</b></p>
<div class="example-contents"><pre class="programlisting">&lt;?php
require_once "Zend/OpenId/Consumer.php";
require_once "Zend/Auth.php";
require_once "Zend/Auth/Adapter/Interface.php";

class OpenIdAdapter implements Zend_Auth_Adapter_Interface {
    private $_id = null;

    public function __construct($id = null) {
        $this-&gt;_id = $id;
    }

    public function authenticate() {
        $id = $this-&gt;_id;
        if (!empty($id)) {
            $consumer = new Zend_OpenId_Consumer();
            if (!$consumer-&gt;login($id)) {
                $ret = false;
                $msg = "Authentication failed.";
            }
        } else {
            $consumer = new Zend_OpenId_Consumer();
            if ($consumer-&gt;verify($_GET, $id)) {
                $ret = true;
                $msg = "Authentication successful";
            } else {
                $ret = false;
                $msg = "Authentication failed";
            }
        }
        return new Zend_Auth_Result($ret, $id, array($msg));
    }
}

$status = "";
$auth = Zend_Auth::getInstance();
if ((isset($_POST['openid_action']) &amp;&amp;
     $_POST['openid_action'] == "login" &amp;&amp;
     !empty($_POST['openid_identifier'])) ||
    isset($_GET['openid_mode'])) {
    $adapter = new OpenIdAdapter(@$_POST['openid_identifier']);
    $result = $auth-&gt;authenticate($adapter);
    if ($result-&gt;isValid()) {
        Zend_OpenId::redirect(Zend_OpenId::selfURL());
    } else {
        $auth-&gt;clearIdentity();
        foreach ($result-&gt;getMessages() as $message) {
            $status .= "$message&lt;br&gt;\n";
        }
    }
} else if ($auth-&gt;hasIdentity()) {
    if (isset($_POST['openid_action']) &amp;&amp;
        $_POST['openid_action'] == "logout") {
        $auth-&gt;clearIdentity();
    } else {
        $status = "Yoy are logged-in as " . $auth-&gt;getIdentity() . "&lt;br&gt;\n";
    }
}
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;?php echo htmlspecialchars($status);?&gt;
&lt;form method="post"&gt;&lt;fieldset&gt;
&lt;legend&gt;OpenID Login&lt;/legend&gt;
&lt;input type="text" name="openid_identifier" value=""&gt;
&lt;input type="submit" name="openid_action" value="login"&gt;
&lt;input type="submit" name="openid_action" value="logout"&gt;
&lt;/fieldset&gt;&lt;/form&gt;&lt;/body&gt;&lt;/html&gt;
            </pre></div>
</div>
<br class="example-break"><p>
            With <code class="code">Zend_Auth</code> the end-user's identity is saved in the
            session's data. It may be checked with <code class="code">Zend_Auth::hasIdentity</code>
            and <code class="code">Zend_Auth::getIdentity</code>.
        </p>
</div>
<div class="sect2" lang="en">
<div class="titlepage"><div><div><h3 class="title">
<a name="zend.openid.consumer.mvc"></a>31.2.8. Integration with Zend_Controller</h3></div></div></div>
<p>
            Finally a couple of words about integration into
            Model-View-Controller applications. Such Zend Framework applications are
            implemented using the <code class="code">Zend_Controller</code> class and they use
            objects of the <code class="code">Zend_Controller_Response_Http</code> class to prepare
            HTTP responses and send them back to the end user's web-browser.
        </p>
<p>
            <code class="code">Zend_OpenId_Consumer</code> doesn't provide any GUI
            capabilities but it performs HTTP redirections on success of
            <code class="code">Zend_OpenId_Consumer::login</code> and
            <code class="code">Zend_OpenId_Consumer::check</code>. These redirections, may work
            incorrectly or not work at all if some data was already sent to the
            web-browser. To properly perform HTTP redirection in MVC code the real
            <code class="code">Zend_Controller_Response_Http</code> should be sent to
            <code class="code">Zend_OpenId_Consumer::login</code> or
            <code class="code">Zend_OpenId_Consumer::check</code> as the last argument.
        </p>
</div>
</div>
<div class="navfooter"><table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="zend.openid.html">Prev</a> </td>
<td width="20%" align="center"><a accesskey="u" href="zend.openid.html">Up</a></td>
<td width="40%" align="right"> <a accesskey="n" href="zend.openid.provider.html">Next</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">Chapter 31. Zend_OpenId </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td>
<td width="40%" align="right" valign="top"> 31.3. Zend_OpenId_Provider</td>
</tr>
</table></div>
<div class="revinfo"></div>
</body>
</html>
